跳到主要内容

1.5-扩展数据类型

Create by fall on 13 Jul 2021 Recently revised in 27 Jun 2023

Array

Array 不是基本数据类型,是一个对象。

与其他编程语言中的数组一样,支持在单个变量名下存储多个元素,并具有执行常见数组操作的成员。

// 创建一个数组
var arr = [11,54,'啥都没输入']

实例方法

数组信息

  • Array.prototype.length 当前数组长度
  • Array.prototype.at() 数组的某一个位置
const arr = [99,66,88,44]
arr.length // 4
arr.at(-1) // 44

const int8 = new Int8Array([0, 10, 42, -10]);
// first element
int8.at(0); // 0
// last element
int8.at(-1); // -10

数组操作

  • Array.prototype.push() 从末尾进行添加
  • Array.prototype.pop() 从末尾取出
  • Array.prototype.shift() 从最开始取出
  • Array.prototype.unshift() 从最开始插入
  • Array.prototype.concat() 合并两个数组
const arr = [1,2,3]
let length = arr.push(4) // 会返回 push 之后数组的长度,此时为 4
const num = arr.pop()
arr.unshift(0)
const start = arr.shift()
arr.concat(2,2,[5]) // 连接的时候,会自动去掉内容中最外层的一个 []
arr.concat([5,7,7])

数组的覆盖

Array.prototype.copyWithin(replaceStart,targetStart,targetEnd)

  • i 必填:从数组的哪个下标开始替换,为负数,从后向前数
  • 可选:targetStart,targetEnd 开始填充的位置(默认为 0),结束填充的位置(默认为原数组长度)

使用当前数组,复制当前数组的内容(从 [j,k)),填充到当前数组

[1,2,3,4,5].copyWithin(2) // [1,2,1,2,3]
[1,2,3,4,5].copyWithin(-2) // [1,2,3,1,2]
[1,2,3,4,5].copyWithin(2,1) // [1,2,2,3,4]
[1,2,3,4,5].copyWithin(2,1,2) // [1,2,2,4,5]

Array.prototype.fill(content,start,end)

  • content 表示将要覆盖的内容
  • start, end 开始和结束的索引
[1,2,3,4,5].fill('z',1,3) 
// [ 1, "z", "z", 4, 5 ]

把数组拍平

Array.prototype.flat()

把二维数组,或者是数组中嵌套数组的内容拍平

[1,2,[3,4,[5]]].flat() // [ 1, 2, 3, 4, [5] ]
// 通过 Infinity 可以直接全部展开为一维数组
[1,2,[3,4,[5]]].flat(Infinity) //[ 1, 2, 3, 4, 5 ]

截取数组

Array.prototype.splice(start,num,in)

该方法会从原数组上截取内容并且添加内容,并返回截取的内容(可以同时进行增、删、改)

  • start 开始的项
  • num 有几项。为零,表示不截取
  • in 插入的内容
  • result 返回截取的内容
let arr = [1,2,3]
// 删除
arr.splice(1,1)
// 插入
arr.splice(1,0,...['loa'])
// 替换
arr.splice(1,1,...['fake'])

Array.prototype.slice(start,end)

不会更改数组,只会返回截取的内容

参数都可以为负数,表示从后向前的位数,但截取 start end 规则不变

  • start 截取的开始位置
  • end 截取的结束位置
let arr = [5,6,4,6,2]
arr.slice() // [5,6,4,6,2]
arr.slice(2,3) // [4]

数组方法

  • Array.prototype.forEach 遍历数组,无返回值
  • Array.prototype.map 每一个值都做指定操作
    • Array.prototype.flatMap 遍历数组,返回数组会去掉一个括号,可以增加数组的长度
  • Array.prototype.filter 筛选出返回值为 true 的所有成员
  • Array.prototype.reduce 把所有的值从前往后两两操作
    • Array.prototype.reduceRight 功能和 reduce 相同,只不过方向相反
  • Array.prototype.every 每一项都为 true,则返回 true
  • Array.prototype.some 任意一项为 ture 返回 true
  • Array.prototype.find 遍历数组,查找到符合条件的内容
    • Array.prototype.findIndex 遍历数组,返回符合的下标
    • Array.prototype.findLast 遍历数组,返回最后一个符合的内容
    • Array.prototype.findLastIndex 遍历数组,返回最后一个符合条件的内容的下标
let arr = [5,6,4,6,2]

// Array.prototype.map
let arr1 = arr.map(item=>item+2) //[ 7, 8, 6, 8, 4 ]
const staffs = [
{ name: "liu", age: 22 },
{ name: "zhang", age: 23 },
]
const staffInfo = staffs.map((staff) => {
return `员工:${staff.name},今年${staff.age}`;
});

// Array.prototype.filter
// 对数组的每一项进行筛选,返回符合条件的内容
let arr2 = arr.filter(item=>item>5) // [ 6, 6 ]

// Array.prototype.reduce 前一项和后一项进行操作,直至两两操作完成
let arr3 = arr.reduce((item1,item2)=>item1+item2) // 23

// every some 返回的是 true & false
let result = [1, 6, 9].every(item=>item<5) // 所有内容都需要符合条件,返回 true :且
let result = [1, 6, 9].some(item=>item>8) // 只要有一个内容符合条件即可:或

// Array.prototype.forEach
[1, 6, 9].forEacg(item=>console.log(item))

// Array.prototype.flatMap 的使用
let arr = [5,[4,6],2]
const arrFlatMap = arr.flatMap(item=>{
console.log(item)
return item // 除去一层数组符号
})
arrFlatMap // [ 5, 4, 6, 2 ]
const arrFlatMap1 = arr.flatMap(item=>{
console.log(item)
return [1,item] // 在每个 item 左侧都插入一个 1
})
arrFlatMap1 // [ 1, 5, 1, [4, 6], 1, 2 ]
// find, findIndex 用于查找内容和查找下标

[1, 6, 9].find((val, index, arr) => val % 2 === 0) // 返回 val
[1, 6, 9].findIndex((val, index, arr) => val % 2 === 0) // 返回 index

查找

查找并且返回 index

Array.prototype.indexOf(content,index) 从开始进行查找

Array.prototype.lastIndexOf() 从后向前查找

  • content 查找的内容
  • index 开始的查找点
[1,64,55,64,55,22,6].indexOf(55)
[1,64,55,64,55,22,6].lastIndexOf(55)

Array.prototype.includes(content)

当前数组是否包含所查找 content 内容

// 想要查看数组中是否包含某个元素 x,
// 原来的做法
if([].indexOf(x)>-1){}
if(~[].indexOf(x)){}
// 现在的做法
if([].includes(x))

可迭代对象

Array.prototype.keys() Array.prototype.values() Array.prototype.entries() 返回可迭代的键、值、键值对

var cc = ["1","2","6","66"]
for(i of cc.keys()){
console.log(i); // 1,2,3,4
}
for(i of cc.values()){
console.log(i); // "1","2","6","66"
}
for(i of cc.entries()){
console.log(i); // [ 0, "1" ] [ 1, "2" ] [ 2, "6" ] [ 3, "66" ]
}

并且可以检验数组中是否包含 NaN,indexOf使用的是 === 全等,无法在遇到 NaN 时返回 index。

[1, NaN, 3].indexOf(NaN)   // -1
[1, NaN, 3].includes(NaN) // true

数组排序

Array.prototype.reverse() 数组倒序

Array.prototype.sort() 数组排序

const arr = [2,51,7,64,79,34]
arr.reverse() // [34, 79, 64, 7, 51, 2]
const arr1 = [2,51,7,64,79,34]
arr1.sort() // [2, 34, 51, 64, 7, 79]
const arr2 = [[2,22],[51,63],[7,82],[64,55],[79,99],[34,2]]
// sort 会根据返回值进行左右排列
// -1 左侧排到左侧 || 0 保持不变 || 1 左侧排到右边
arr2.sort((left,right)=>{
// return left[0]-right[0]
if(left[0]>right[0]){ // 左侧大于右侧
return 1 // left 值会放在右侧
}else{
return -1
}
}) // [ 2, 7, 34, 51, 64, 79 ]

数组转换

Array.prototype.toString() 转换为字符串返回

Array.prototype.toLocaleString() 转换为本地格式字符串返回

Array.prototype.join() 用指定的字符分割数组,并转换为字符串

ES 2023

这些内容需要 chrome 版本在 110 以上

原本的排序,移除,反序列,都是在原来的数组上进行操作,现在可以直接生成新的数组

Array.prototype.toSorted()

const languages = ["JavaScript", "TypeScript", "CoffeeScript"];
const sorted = languages.toSorted();
console.log(sorted);
// [ 'CoffeeScript', 'JavaScript', 'TypeScript' ]
console.log(languages);
// => [ 'CoffeeScript', 'TypeScript', 'JavaScript' ]
const spliced = languages.toSpliced(2, 1, "Dart", "WebAssembly");
console.log(spliced);
// => [ 'JavaScript', 'TypeScript', 'Dart', 'WebAssembly' ]

Array.prototype.toSplice(start,num,in)

从原数组上 start 开始截取 num 长度的内容并且添加 in 内容,返回新的数组(可以同时进行增、删、改,且不改变原数组)

  • start 开始的项
  • num 有几项。为零,表示不截取
  • in 插入的内容
  • result 返回截取的内容
let arr = [1,2,3]
// 删除
arr.toSplice(1,1)
// 插入
arr.toSplice(1,0,...['loa'])
// 替换
arr.toSplice(1,1,...['fake'])

Array.prototype.toReverse()

const languages = [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
const reversed = languages.toReversed();
console.log(reversed); // ['CoffeeScript', 'TypeScript','JavaScript' ]
console.log(languages); // [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]

Array.prototype.with()

更新数组指定 index 的内容并返回更新后的数组(不修改原数组)

const languages = [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]
const after = languages.with(2,"rust");
console.log(after); // [ 'JavaScript', 'TypeScript', 'rust' ]
console.log(languages); // [ 'JavaScript', 'TypeScript', 'CoffeeScript' ]

静态方法

Array.isArray() 用来判断是否是数组

Array.isArray([]) // true
let ad = {
[0]:'vc',
length:1
}
Array.isArray(ad)

Array.from() 可以将字符串,类数组对象( NodeListarguments),Set 和可迭代对象转化为数组

// 字符串转数组
Array.from("我搜搜") // [ "我", "搜", "搜" ]
// 数组合并,去重
let merge = [...[1,2,3],...[1,5,3]]
Array.from(new Set(merge))
function fun(){// 无论传入多少个数据,都可以拼接为数组,然后返回
return Array.from(arguments)
}
fun(1,5,6)
// 除此之外,第二个参数可以将每一个数据都进行一次操作
Array.from("我搜搜",x=>x+"_") // ["我_", "搜_", "搜_"]
Array.from({length: 3}, (·v, i) => ++i) // [1, 2, 3]
const map = new Map([["name","fall"],["age",23]])
Array.from(map) // [["name","fall"],["age",23]]

Array.of() 可以将参数转换为数组

Array.of('dfdfdfdf',5,64,79) // [ "dfdfdfdf", 5, 64, 79 ]

Set

Set 是一个键值统一的数据结构,可以近似理解为是数组,只不过数组的下标和数组的单项内容相同,可以通过传入一个数组,实现对于数组的构造。

const cc = new Set([1,2,2])
cc // Set([1,2])
const ss = new Set('unique')
ss // Set('u','n','i','q','e')

Set 内部判断两个值是否相等使用的是 sameValueZero 算法,需要注意条件和 === 差不多,只有一个例外,NaNNaN 会被认为相同。

let cc = new Set([NaN,NaN])
cc.size // 1
// 只要对象不指向同一个地址,就不会因为重复被筛选掉
var a = {title:'leaf'}
var b = {title:'leaf'}
var c = a
let set1 = new Set()
set1.add(a);set1.add(b);
set1.size // 2
let set2 = new Set()
set2.add(a);set2.add(c)
set2.size // 1
let set3 = new Set()
set3.add(a);set3.add({title:'leaf'})
set3.size // 2

Set 是有顺序的,根据添加的顺序进行排序,通过 for...of... 进行遍历

let set = new Set([1,3])
set.add('cs')
set.add('vc')
for(item of set){
console.log(item) // 1 3 'cs' "vc"
}

实例属性

let set = new Set([1,2])
set.add(5) // 向set内添加内容,返回 set
set.size // 返回set的个数
set.delete(2) // 删除元素,成功会返回true
set.has(2) // 是否拥有 2
set.keys() // 返回简明的遍历器
set.claer() // 清空当前Set

新增的原型方法

2024 年 6 月后,主流浏览器都可以使用该特性,而不需要 polyfill

应用场景

数组去重,字符串去重

Array.from(new Set([1,3,4,2,1,3]))  //[1,3,4,2]
[...new Set([1,3,4,2,1,3])] //[1,3,4,2]

[...new Set('unique')].join('') // uniqe

求交集,差集,并集

let a = new Set('asd')
let b = new Set('asc')
let union = new Set([...a,...b]) // 并集
// 交集
let intersect = new Set([...a].filter(x=>b.has(x)))
// 差集,a-b的差集
let diff = new Set([...a].filter(x=>!b.has(x)))

WeakSet

和 Set 差不多,但是只能进行存储对象 WeakSet 的成员只能是对象,而不能是其他的值

  • WeakSet 只能是对象的集合,而不能是任何类型的任意值;

  • WeakSet 持弱引用:集合中对象的引用为弱引用。如果没有其他的对 WeakSet 中对象的引用,那么这些对象会被当成垃圾回收掉。这也意味着 WeakSet 中没有存储当前对象的列表。正因为这样,WeakSet 是不可枚举的,也就没有 size 属性,没有 clear 和遍历的方法。

  • WeakSet.prototype.add(value):添加一个新元素 value

  • WeakSet.prototype.delete(value):从该 WeakSet 对象中删除 value 这个元素;

  • WeakSet.prototype.has(value):返回一个布尔值, 表示给定的值 value 是否存在于这个 WeakSet 中;

Map

不同于对象,对象的键只能是字符串或者 Symbol,而 Map 的键可以是任何类型(原始类型、对象或者函数),可以通过 Map 构造函数创建一个实例,入参是具有 Iterator 接口且每个成员都是一个双元素数组 [key, value] 的数据结构:

// Map的声明和添加
const map = new Map()
map.set({},12) // 设置一个键值对,可以通过此方法无线设置键值对
// 并且可以通过二元数组设置为Map
var arr = [['name',"fall"],['age',23]]
const map1 = new Map(arr)

运算特性

如果键值出现了重复,后面的数据会覆盖前面的数据

let map = new Map()
let foo = {foo: 'foo'}
map.set(foo, 'foo1') // 此处 foo作为键进行使用
map.set(foo, 'foo2')
map.get(foo) // 'foo2'

因为同 set 使用同样的 sameValueZero 算法,所以对于键名同为 NaN 以及相同对象而不同实例的处理同 Set

let a = NaN
let b = NaN
let map = new Map()
map.set(a, 'a')
map.set(b, 'b')
map.size // 1
map.get(a) // 'b'

原型方法

Map.prototype.size:返回 Map 对象的键值对数量

Map.prototype.set(key, value):设置 Map 对象中键的值。返回该 Map 对象

Map.prototype.get(key): 返回键对应的值,如果不存在,则返回 undefined

Map.prototype.has(key):返回一个布尔值,表示 Map 实例是否包含键对应的值

Map.prototype.delete(key): 如果 Map 对象中存在该元素,则移除它并返回 true

Map.prototype.clear(): 移除 Map 对象的所有键/值对

Map.prototype.keys():返回一个新的 Iterator 对象, 它按插入顺序包含了 Map 对象中每个元素的键

Map.prototype.values():返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值

Map.prototype.entries():返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组

Map.prototype.forEach(callbackFn[, thisArg]):按插入顺序遍历 Map

let map = new Map()
map.set({a: 1}, 'a')
map.set({a: 2}, 'b')
for (let [key, value] of map) {
console.log(key, value)
}
// {a: 1} 'a'
// {a: 2} 'b'
for (let key of map.keys()) {
console.log(key)
// {a: 1}
// {a: 2}
}

WeakMap

  • 只受对象(null 除外)作为键,键名所指的对象,不计入垃圾回收机制
  • 类似于 Map 的结构, WeakMap 是不能被迭代的。
let wm = new WeakMap()
let foo = {name: 'foo'}
wm.set(foo, 'a') // Weak
wm.get(foo) // 'a'
wm.has(foo) // true

虽然 wm 的键对 foo 对象有引用,但是丝毫不会阻止 foo 对象被 GC 回收。当引用对象 foo 被垃圾回收之后,wmfoo 键值对也会自动移除,所以不用手动删除引用。

实例方法

  • WeakMap.prototype.delete(key):移除 key 的关联对象
  • WeakMap.prototype.get(key):返回 key 关联对象, 或者 undefined(没有key关联对象时)
  • WeakMap.prototype.has(key):根据是否有 key 关联对象返回一个 Boolean
  • WeakMap.prototype.set(key, value):在 WeakMap 中设置一组 key 关联对象,返回这个 WeakMap 对象

集合

有关集合的详细内容可查看存储扩展对象文件。

set

  • 键值对相同
  • 通过 add 方法进行添加
  • 所有值不重复
let imgs = new Set()
imgs.add(100)
imgs.add('hello')
imgs.add('hello')
imgs.add(true)
imgs.add(new String('world'))

WeakSet 同 Set,但只能是对象的集合,意思就是 WeakSet 中的东西是弱引用,如果没有其他的对 WeakSet 中对象的引用,这些对象会被垃圾回收掉。

map

Map 集合的特点

  • 通过键值区分不同的 Map
  • 通过 add 方法进行添加
  • map 集合传值必须使用键值对进行添加
let map = new Map();
map.set('张三',"吃饭的");
map.set("六六","喝水的");
map.set("六六","吃药了");
console.log(map);
// Map对象取值
console.log(map.get('六六')); // 吃药了

WeakMap 同 Set,意思就是 WeakMap 中的东西垃圾回收时不考虑,使用它不用担心内存泄漏问题

遍历

数组,和类数组对象(Map,Set)的遍历

数组的遍历

var arr = [10,22,33,44,55,66];
//1. for 循环
for(var i = 0;i<arr.length;i++){
document.write('for循环'+i+','+arr[i])
}
//2. for in 此时 i 指的是 index
for(var i in arr){
document.write('for in 循环'+ i+','+ arr[i])
}
//3. forEach
arr.forEach((item,index,arr)=>{
document.write('forEach'+index+','+item)
})
// 4. for...of
for(item of arr){
document.write("for...of"+item +"<br>")
}

map 的遍历

集合使用 for...in 不会出任何结果

var map = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
// 对map进行循环
for (let item of map) {
console.log(item[0],item[1])
}
// 将item解构
for(let [key,value] of map){
console.log(key,value)
}

set 的遍历

var mySet = new Set(['闪电','flash','too'])
console.log(mySet)
mySet.forEach(item => {
console.log(item)
})
mySet.forEach(function(item){
console.log(item)
})
// 以键值对的方式进行循环
for (let item of mySet.entries()) {
console.log(item)
}
// 以键的方式进行循环
for (let item of mySet.keys()) {
console.log(item)
}

扩展运算符

数据扩展运算符 ... 可以展开数组,以及 set

// 应用一:函数传参
Math.max(...[1, 2, 3]) // 3
// 应用二:数组合并
let merge = [...[1, 2], ...[3, 4], 5, 6] // 1, 2, 3, 4, 5, 6
// 应用三:浅克隆
let a = [1, 2, 3]
let clone = [...a]
a === clone // false
// 应用四:数组解构
const [x, ...y] = [1, 2, 3]
x // 1
y // [2, 3]

如果数组上没有值,ES6 新增语法就会将空位默认替换为 undefined

[...[1, , 3].values()]  // [1, undefined, 3]

参考文章

作者文章名称
Phil NashES2023 introduces new array copying methods to JavaScript
Mozilla 官方文档https://developer.mozilla.org/en-US/blog/javascript-set-methods/